﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using NUnit.Framework;
using WeBlog.Controllers;
using WeBlog.DataContext;
using WeBlog.Interfaces;
using WeBlog.Models;
using WeBlog.Repositories;
using WeBlog.Resources;
using WeBlog.Services;
using WeBlog.Tests.Helpers;
using WeBlog.Tests.Mocks;

namespace WeBlog.Tests.Controllers {
	[TestFixture]
	public class PostControllerTests
	{
		private IUnitOfWork _unitOfWork;
		private IEmailService _emailService;
		private User _admin = new User { UserId = 100, Email = "admin@weblog.com", FirstName = "admin", LastName = "admin", Activated = true };
		private User _guest = new User { UserId = 200, Email = "guest@weblog.com", FirstName = "guest", LastName = "guest", Activated = true };
		private MockPrincipal _adminPrincipal = new MockPrincipal(new MockIdentity("admin@weblog.com"), new[] {Role.Admin});
		private MockPrincipal _guestPrincipal = new MockPrincipal(new MockIdentity("guest@weblog.com"), new[] {Role.Subscriber});

		[SetUp]
		public void Setup()
		{
			_unitOfWork = new InMemoryUnitOfWork();
			var userRepository = new UserRepository(_unitOfWork);
			_emailService = new FakeEmailService();

			userRepository.Add(_admin);
			userRepository.Add(_guest);
		}

		[Test]        
		public void Index_Should_Return_Valid_View()
		{
			//Arrange
			var controller = new PostController(_unitOfWork, _emailService);            
			controller.SetFakeControllerContext();

			//Act
			var result = controller.Index(new GridSettings("Title")) as ViewResult;

			//Assert
			Assert.IsInstanceOf(typeof(PostListViewModel), result.ViewData.Model);            
		}

		[Test]
		public void Manage_Should_Return_Valid_View() 
		{
			//Arrange
		    var controller = new PostController(_unitOfWork, _emailService) {CurrentUser = _admin};
		    controller.SetFakeControllerContext(_adminPrincipal, _admin);
			var settings = new GridSettings("PublishDate");

			//Act
			var result = controller.Manage(settings) as ViewResult;

			//Assert
			Assert.IsInstanceOf( typeof(ManagePostsViewModel), result.ViewData.Model);
		}

		[Test]
		public void Details_Should_Return_NotFound_For_Invalid_Slug() {
			//Arrange
			var controller = new PostController(_unitOfWork, _emailService);
			//controller.SetFakeControllerContext();			
			
			//Act

			//Assert
			Assert.Throws(typeof(NotFoundException), () => controller.Details("the-post-d0es-not-exist"));
		}

		[Test]
		public void Details_Should_Return_Valid_Model_For_Valid_Slug()
		{
			//Arrange
			var postRepository = new PostRepository(_unitOfWork);
			var post1 = new Post { Slug = "Hello", PostId = 1, PostComments = new List<PostComment>() };
			var post2 = new Post { Slug = "Goodbye", PostId = 2, PostComments = new List<PostComment>() };
			postRepository.Add( post1 );
			postRepository.Add( post2 );

			var controller = new PostController(_unitOfWork, _emailService);            

			//Act
			var result = controller.Details("Goodbye") as ViewResult;            

			//Assert
			Assert.IsNotNull(result);
			Assert.IsInstanceOf(typeof(PostDetailsViewModel), result.Model );
			Assert.AreEqual(((PostDetailsViewModel)result.Model).Post.Slug, "Goodbye");
		}

		[Test]
		public void Details_Should_Save_PostVisit_When_TrackPostVisits_Is_On()
		{
			//Arrange
			var repository = new PostRepository(_unitOfWork);
			var post = new Post { Slug = "TestPost", PostId = 3, PostComments = new List<PostComment>(), PostVisits = new List<PostVisit>() };
			repository.Add(post);
			
			SiteManager.CurrentConfiguration.TrackPostVisits = true;
			var controller = new PostController(_unitOfWork, _emailService);

			//Act
			controller.Details("TestPost");

			//Assert
			var testPost = repository.FindBySlug("TestPost");
			Assert.AreEqual(1, testPost.PostVisits.Count);
		}

		[Test]
		public void Permalink_Should_Redirect_To_Proper_Action_For_Valid_PostId()
		{
			//Arrange
			var postRepository = new PostRepository(_unitOfWork);
			var post1 = new Post { Slug = "Hello", PostId = 1, PostComments = new List<PostComment>() };            
			postRepository.Add(post1);            

			var controller = new PostController(_unitOfWork, _emailService);

			//Act
			var result = controller.Permalink(1) as RedirectToRouteResult;

			//Assert
			Assert.IsNotNull(result);
			Assert.AreEqual("Details", result.RouteValues["action"]);
		}

		[Test]
		public void Permalink_Action_Should_Return_NotFound_For_Invalid_PostId()
		{
			//Arrange
			var controller = new PostController(_unitOfWork, _emailService);

			//Act
			var result = controller.Permalink(100) as ViewResult;

			//Assert
			Assert.AreEqual("NotFound", result.ViewName);
		}

		[Test]
		public void Create_Should_Return_Permission_Denied_For_Unauthorized_User()
		{
			//Arrange
			var controller = new PostController(_unitOfWork, _emailService);
			controller.SetFakeControllerContext(_guestPrincipal, _guest);

			//Act
			var result = controller.Create() as ViewResult;

			//Assert            
			Assert.AreEqual("PermissionDenied", result.ViewName);
		}

		[Test]
		public void Create_Should_Return_Permission_Denied_For_Unauthorized_User2()
		{
			//Arrange
			var postRepository = new PostRepository(_unitOfWork);
			var categoryRepository = new CategoryRepository(_unitOfWork);
			var post = new Post { PostId = 10, Title = "MyPost", Content = "foobar", AuthorId = 1 };
			categoryRepository.Add(new Category { CategoryId = 3, Name = "category3", Description = "category3" });
			categoryRepository.Add(new Category { CategoryId = 4, Name = "category4", Description = "category4" });
			
			var controller = new PostController(_unitOfWork, _emailService);
			controller.SetFakeControllerContext(_guestPrincipal, _guest);

			//Act
			var result = controller.Create(post, "tag1,tag2", new int[] { 3, 4 }) as ViewResult;

			//Assert            
			Assert.AreEqual("PermissionDenied", result.ViewName);
		}

		[Test]
		public void Create_Should_Return_Valid_For_Model_For_Authorized_User()
		{
			//Arrange
			var controller = new PostController(_unitOfWork, _emailService);            
			controller.SetFakeControllerContext(_adminPrincipal, _admin);

			//Act
			var result = controller.Create() as ViewResult;

			//Assert
			Assert.IsInstanceOf( typeof(PostFormViewModel), result.ViewData.Model);
		}

		[Test]
		public void Create_Should_Have_Model_Errors_When_Slug_or_Title_Already_Exists()
		{
			//Arrange
			var postRepository = new PostRepository(_unitOfWork);
			var controller = new PostController(_unitOfWork, _emailService);
			controller.SetFakeControllerContext(_adminPrincipal, _admin);

			var post = new Post {PostId = 1, Slug = "foo", Title = "bar"};
			postRepository.Add( post );

			//Act
			var result = controller.Create( post, String.Empty, new int[] {} ) as ViewResult;

			//Assert
			Assert.AreEqual(result.ViewData.ModelState["Slug"].Errors.Count, 1 );
			Assert.AreEqual(result.ViewData.ModelState["Title"].Errors.Count, 1);
		}

		[Test]
		public void Delete_Should_Remove_Record_When_User_Is_Authorized()
		{
			//Arrange
			var postRepository = new PostRepository(_unitOfWork);
			var controller = new PostController(_unitOfWork, _emailService);
			controller.SetFakeControllerContext(_adminPrincipal, _admin);            
			var post = new Post { PostId = 5, Slug = "foo", Title = "bar", Categories= new List<Category>(), Tags = new List<Tag>() };
			postRepository.Add(post);

			//Act
			var result = controller.Delete(5) as JsonResult;

			//Assert
			Assert.IsTrue( ((dynamic)result.Data).Success );
			Assert.Null( postRepository.Find(1));
		}

		[Test]
		public void Delete_Should_Return_PermissionDenied_When_User_Is_Not_Authorized()
		{
			//Arrange
			var postRepository = new PostRepository(_unitOfWork);
			var controller = new PostController(_unitOfWork, _emailService);
			controller.SetFakeControllerContext(_guestPrincipal,_guest);            
			var post = new Post { PostId = 1, Slug = "foo", Title = "bar" };
			postRepository.Add(post);
			
			//Act
			var result = controller.Delete(1);

			//Assert
			Assert.IsFalse(((dynamic)result.Data).Success);
			Assert.AreEqual(((dynamic)result.Data).Message, Strings.Validation_PermissionDenied);       
		}

		[Test]
		public void Create_Should_Succeed_When_Input_Valid()
		{
			//Arrange
			var postRepository = new PostRepository(_unitOfWork);
			var categoryRepository = new CategoryRepository(_unitOfWork);
			var controller = new PostController(_unitOfWork, _emailService);
			controller.SetFakeControllerContext(_adminPrincipal, _admin);
			var post = new Post { PostId = 1, Title = "bar", Content = "foobar", AuthorId = 1 };
			categoryRepository.Add(new Category { CategoryId = 1, Name = "category1", Description = "category1" });
			categoryRepository.Add(new Category { CategoryId = 2, Name = "category2", Description = "category2" });

			//Act
			var result = controller.Create(post, "tag1,tag2", new int[] { 1, 2 });

			//Assert                        
			var inserted = postRepository.FindBySlug("bar");
			Assert.IsInstanceOf(typeof(RedirectToRouteResult), result);
			Assert.AreEqual(inserted.Tags.Count, 2 );
			Assert.AreEqual(inserted.Categories.Count, 2);
			Assert.AreEqual(inserted.AuthorId, 1);
		}

		[Test]
		public void Edit_Should_Return_Permission_Denied_For_Unauthorized_User() 
		{
			//Arrange
			var postRepository = new PostRepository(_unitOfWork);
			var post = new Post { PostId = 1, Slug = "foo", Title = "bar", Content = "foobar", AuthorId = 1 };
			postRepository.Add(post);
			var controller = new PostController(_unitOfWork, _emailService);
			controller.SetFakeControllerContext(_guestPrincipal, _guest);

			//Act
			var result = controller.Edit(1) as ViewResult;

			//Assert
			Assert.AreEqual("PermissionDenied", result.ViewName);
		}

		[Test]
		public void Edit_Should_Return_Valid_Model_For_Authorized_User()
		{
			//Arrange
			var postRepository = new PostRepository(_unitOfWork);
			var post = new Post { PostId = 1, Slug = "foo", Title = "bar", Content = "foobar", AuthorId = 1 };
			postRepository.Add(post);
			var controller = new PostController(_unitOfWork, _emailService);
			controller.SetFakeControllerContext(_adminPrincipal, _admin);
			
			//Act
			var result = controller.Edit(1) as ViewResult;

			//Assert
			Assert.IsInstanceOf( typeof(PostFormViewModel), result.ViewData.Model);
		}

		[Test]
		public void Edit_Should_Return_NotFound_For_Invalid_Post() 
		{
			//Arrange
			var controller = new PostController(_unitOfWork, _emailService);	

			//Act			

			//Assert
			Assert.Throws(typeof(NotFoundException), () => controller.Edit(2));
		}

		[Test]
		public void Edit_Should_Update_Post()
		{
			//Arrange
			var formValues = new Dictionary<string, string>();
			formValues.Add("Post.Content", "My content is here");

			var categoryRepository = new CategoryRepository(_unitOfWork);
			categoryRepository.Add(new Category { CategoryId = 1, Name = "category1", Description = "category1" });
			categoryRepository.Add(new Category { CategoryId = 2, Name = "category2", Description = "category2" });

			var tagRepository = new TagRepository(_unitOfWork);
			tagRepository.Add(new Tag { TagId = 1, Name = "tag1", Posts = new List<Post>() });
			tagRepository.Add(new Tag { TagId = 2, Name = "tag2", Posts = new List<Post>() });

			var postRepository = new PostRepository(_unitOfWork);
			var post = new Post { PostId = 1, Slug = "foo", Title = "bar", Content = "foobar", Tags = new List<Tag>() { tagRepository.Find(1) }, Categories = new List<Category>() { categoryRepository.FindById(1)  }, Published = true };
			postRepository.Add(post);

			var controller = new PostController(_unitOfWork, _emailService);
			controller.SetFakeControllerContext(_adminPrincipal, _admin);
			controller.ValueProvider = MvcMockHelpers.SetupValueProvider(formValues);

			//Act
			controller.Edit(1, "tag1, tag2", new int[] { 1, 2 });

			//Assert
			var testPost = postRepository.Find(1);
			Assert.AreEqual("My content is here", testPost.Content);
			Assert.AreEqual(2, testPost.Tags.Count);
			Assert.AreEqual(2, testPost.Categories.Count);
		}

		[Test]
		public void Edit_Should_Return_PermissionDenied_When_User_Does_Not_Have_Permissions()
		{
			//Arrange
			var controller = new PostController(_unitOfWork, _emailService);
			controller.SetFakeControllerContext(_guestPrincipal, _guest);

			//Act
			var result = controller.Edit(1, "tag1, tag2", new int[] { 1, 2 }) as ViewResult;

			//Assert
			Assert.AreEqual("PermissionDenied", result.ViewName);
		}

		[Test]
		public void Save_Rating_Inserts_Record_Successfully()
		{
			//Arrange
			var postRepository = new PostRepository(_unitOfWork);
			var post = new Post { PostId = 1, Slug = "foo", Title = "bar", Content = "foobar", AuthorId = 1 };
			postRepository.Add(post);
			var controller = new PostController(_unitOfWork, _emailService);
			controller.SetFakeControllerContext(_adminPrincipal, _admin);         		    
			//Act
			var result = controller.SaveRating(1,4);

			//Assert                        
			var updated = postRepository.Find(1);            
			Assert.AreEqual(updated.PostRatings.Count, 1);
			Assert.AreEqual(updated.PostRatings.First().Rating, 4);
		}

		[Test]
		public void Flag_Action_Should_Succeed()
		{
			//Arrange
			var postRepository = new PostRepository(_unitOfWork);
			var post1 = new Post { Slug = "MySlugs", PostId = 5, PostComments = new List<PostComment>() };            
			postRepository.Add(post1); 

			var controller = new PostController(_unitOfWork, _emailService);

			//Act
			var result = controller.Flag(5) as JsonResult;

			//Assert
			Assert.IsTrue((result.Data as JsonResponse).Success);
		}

		[Test]
		public void Posts_Per_Page_Should_Equal_Settings_PostsPerPage()
		{
			//Arrange
			var postRepository = new PostRepository(_unitOfWork);
			var startDate = DateTime.Now.Date;
			for (int i = 1; i <= 5; i++)
			{
				var post = new Post { PostId = i, Slug = "post-" + i, Title = "title-" + i, Content = i.ToString(), Published = true, PublishDate = startDate.AddMinutes(1)};
				postRepository.Add(post);
			}
			var controller = new PostController(_unitOfWork, _emailService);
			controller.SetFakeControllerContext();

			//Act
			SiteManager.CurrentConfiguration.PostsPerPage = 5;
			var result = controller.Index(new GridSettings { PageSize = 5}) as ViewResult;

			//Assert
			var model = (PostListViewModel)result.Model;            
			Assert.AreEqual(5, model.Data.Count());   
		}
	}
}
